{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 理解对象与类：Python 篇"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在前面大套的历史背景和理论概念解说之后，我们来看看 Python 中对面向对象概念的具体实现，主要结合一些代码例子进行说明。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类定义与对象创建"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先我们来看看类的定义和对应对象的创建："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog:\n",
    "\n",
    "    kind = ''\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    def bark(self):\n",
    "        return 'woof-woof'\n",
    "\n",
    "a = Dog('Fido')\n",
    "b = Dog('Buddy')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面这段代码逐行解释如下：\n",
    "* 第一行是关键字 `class` 打头，代表类定义的开始，后面是类的名字，然后是一个冒号，表示下面缩进的代码段是 `Dog` 类的定义；在 Python 中类本身也是个对象，叫**类对象**（*class object*）；\n",
    "    * 你现在能感觉到，Python 的冒号通常都是这个作用，前面看到过的 `if` `else` `for` `while` `def` 都类似；\n",
    "* 下面是一个在类定义里直接出现的变量 `kind`，这样定义的变量称为**类变量**（*class variable*），这种变量是整个类共有的，所有该类的对象实例共享；\n",
    "* 下面是一个函数 `__init__()` 的定义，因为这个函数定义在类里，所以通常我们称之为**方法**（*method*）：\n",
    "    * 方法的第一参数必定是 `self`，这是个特殊的变量，代表从这个类实例化出来的对象自己；注意这里是类的定义，还没有真正创建出对象，这个 `self` 相当于对未来产生对象的“提前引用”；类定义中的方法在被调用时解释器都会自动传递这个 `self` 进去；\n",
    "    * 作为一种约定俗成的规矩，类似 `__init__()` 用**双下划线**开头和结尾的方法，属于**特殊方法**（*special method*），是 Python 解释器特别定义和使用的；\n",
    "    * `__init__()` 是解释器在实例化一个对象之后自动调用的方法，用来对新创建的对象实例做初始化；\n",
    "    * 方法可以在 `self` 之后带任意的输入参数，这些输入参数由方法自行使用；\n",
    "    * `self.name` 定义了一个**实例变量**（*instance variable*），前面说了 `self` 代表未来被实例化的对象自身，`.` 表示属于对象的，`name` 则是这个实例变量的名字，这个方法用传进来的参数 `name` 来初始化实例变量 `self.name`，要注意这两个 `name` 是不一样的；\n",
    "* 下面是函数 `bark()` 的定义，这是我们自定义的方法，没有自定义的输入参数（例行的 `self` 参数不算），然后返回 *dog* 的叫声；\n",
    "* 缩进的类定义部分结束，下面是创建这个类的对象实例的方法，`a = Dog('Fido')`：\n",
    "    * 一个类的实例化，就是用这个类作为模板创建一个实际存在的对象，Python 的语法是把类对象 `Dog` 当做一个函数来使用，`Dog('Fido')` 看上去就是个函数，它的意思是：创建一个 `Dog` 类的**对象实例**（*instance object*），并用以参数 `'Fido'` 调用类的 `__init__()` 方法；\n",
    "    * 解释器会在内存中创建这个对象实例，然后用参数 `'Fido'` 调用类的 `__init__(self, name)` 方法，第一个参数 `self` 解释器会自动放进去（就是刚刚创建好的对象实例），而 `name` 参数就是 `'Fido'`，`__init__()` 方法运行完毕就会把这个对象的 `self.name` 实例变量设为 `'Fido'`；\n",
    "    * 实例化执行完毕，将创建的对象赋给 `a` 变量。\n",
    "* 创建第二个对象实例 `b`。\n",
    "\n",
    "创建出来的两个对象实例就可以使用了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'canidae'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 类变量可以通过 class object 来访问，并在所有实例变量之间共享\n",
    "Dog.kind = 'canidae'\n",
    "a.kind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'canidae'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.kind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Fido'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 而实例变量是各个对象实例自己拥有，互不影响\n",
    "a.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Buddy'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'woof-woof'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 当然我们也可以使用我们定义的方法\n",
    "a.bark()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果前面的看上去有点复杂，可以先努力理解和记住下面的几个要点：\n",
    "* 可以用 `class MyClass:` 的语法来定义类；\n",
    "* 类定义中直接定义的变量是类变量（*class variable*），为所有对象实例所共享，可用 `MyClass.xxx` 的语法访问；\n",
    "* 类定义中出现的 `self.xxx` 是实例变量（*instance variable*），是各个对象实例各自的属性（*attribute*），互不影响，对象实例创建后可以用 `obj.xxx` 的语法访问；\n",
    "* 类定义中通常会定义初始化方法 `__init__(self, param1, param2, ...)`，该方法会在对象实例创建后自动被调用，其参数中 `self` 是固定的，而后面如果有其他参数，需要在创建对象实例时传给类对象，类似这样：`obj = MyClass(param1, param2, ...)`，这两个地方的参数表是对应上的；\n",
    "* 类定义中定义的函数是对象实例的方法（*method*），可以用 `obj.method()` 的语法调用；所有方法的第一个参数固定为 `self`，其他参数的使用方法与前述 `__init__` 方法一样。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 第二个类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有狗就有猫，我们现在可以依葫芦画瓢的定义一个猫类出来："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat:\n",
    "\n",
    "    kind = 'felidae'\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    def mew(self):\n",
    "        return 'meow-meow'\n",
    "\n",
    "c = Cat('Garfield')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'felidae'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Cat.kind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Garfield'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'meow-meow'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.mew()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和前面的狗类好像没有太大区别，因为从特征上基本一致：无论是猫还是狗，都有一个类变量 `kind`（动物的分类），以及一个实例变量 `name`（动物的名字），然后有个方法来发出叫声。这时候我们可以想到的是，是不是可以将这两个类的共性特征抽取出来，用一个公共的父类来实现呢？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 父类、子类、继承与多态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先我们定义一个比猫和狗都更加抽象的类，准备用作猫和狗的共通父类，这个类把上面说的共性特征都表达出来，想一下应该怎么写，然后看下面的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal:\n",
    "\n",
    "    kind = ''\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    def voice(self):\n",
    "        return '...'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以看到，`Animal` 类具有类变量 `kind`、实例变量 `self.name` 和一个方法 `voice()`，但是没有给出具体的分类名和叫声是什么。\n",
    "\n",
    "下面我们看看怎么继承这个父类来定义子类，在 Python 中要继承一个已存在的类，使用下面这样的语法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog(Animal):\n",
    "    \n",
    "    kind = 'canidae'\n",
    "    \n",
    "    def voice(self):\n",
    "        return 'woof-woof'\n",
    "    \n",
    "    bark = voice"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`class` 语句中类名后面可以紧跟一个括号，里面是要继承的父类，这样定义出来的子类就直接拥有了父类的一切，但可以修改，上面的定义就修改了类变量 `kind` 的值，给出了 `voice()` 方法的一个狗类实现（“汪汪汪”），并定义了一个自己独有的方法 `bark`（作为 `voice` 方法的一个别名）。下面的猫类也类似："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat(Animal):\n",
    "    \n",
    "    kind = 'felidae'\n",
    "    \n",
    "    def voice(self):\n",
    "        return 'meow-meow'\n",
    "    \n",
    "    mew = voice"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意这两个子类都没写构造方法 `__init__()`，因为父类的就好用，不需要改。现在我们可以使用这两个子类来实例化一些对象出来："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = Animal('キュゥべえ')\n",
    "c = Cat('Garfield')\n",
    "d = Dog('Snoopy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "キュゥべえ : ...\n",
      "Garfield : meow-meow\n",
      "Snoopy : woof-woof\n"
     ]
    }
   ],
   "source": [
    "for animal in [a, c, d]:\n",
    "    print(animal.name, ':', animal.voice())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "仔细看上面的代码，`a` `c` `d` 虽然是不同类的对象实例，但是因为它们都是 `Animal` 或其子类的对象实例，所以拥有 `Animal` 类所定义的标准接口（**继承**），从而可以在一个循环中一视同仁的处理，而且不同类的对象实例自动调用了各自类定义的 `voice()` 方法实现，产生各自正确的输出（**多态**）。\n",
    "\n",
    "所以其实也不复杂吧？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 公有和私有？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和 C++、Java 等面向对象编程语言不一样，Python 并没有私有成员一说，从语法上讲，所有类定义中出现的类变量、对象变量、方法都是公开的。\n",
    "\n",
    "但是 Python 同时给出了一个相对宽松的“弱约束”，就是作为一种约定俗成的代码风格，将类定义中所有下划线 `_` 开头的变量和方法作为“内部”变量和方法来看待，也就是说这些变量和方法属于类的内部实现的一部分，随时可能改变，外部最好不要依赖于这些变量和方法。\n",
    "\n",
    "所以 Python 虽然没有提供强制受限的“私有成员”，但还是遵循了“接口与实现分离”的思维模式，我们在定义一个类的时候，如果有不希望外部直接使用的变量和方法，可以在其名字前加一个下划线 `_`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 了解在 Python 中如何定义类和创建类的对象实例；\n",
    "* 了解在 Python 中的继承和多态的实现方法。\n",
    "\n",
    "现在可以再读一遍前一章的概念，应该会有不同的感受。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
